import json
from PySide6.QtWidgets import (QComboBox, QTreeWidget, QTreeWidgetItem, 
                              QApplication, QWidget, QVBoxLayout, QStyledItemDelegate)
from PySide6.QtCore import Qt, QEvent, QPoint, QSize, QTimer, QObject, Signal

class TreeComboDelegate(QStyledItemDelegate):
    def sizeHint(self, option, index):
        size = super().sizeHint(option, index)
        return QSize(size.width() + 20, size.height())

class TreeCheckableComboBox(QComboBox):
    selection_changed = Signal()
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.tree = QTreeWidget()
        self.tree.setHeaderHidden(True)
        self.tree.setIndentation(15)
        self.tree.setItemDelegate(TreeComboDelegate())
        self._suppress_hide = False  # 关闭抑制标志
        
        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        self.lineEdit().setAlignment(Qt.AlignmentFlag.AlignLeft)
        
        self.setModel(self.tree.model())
        self.setView(self.tree)
        
        self.tree.viewport().installEventFilter(self)
        self.tree.itemChanged.connect(self.handle_item_changed)
        self._popup_open = False

        self.global_event_filter = GlobalEventFilter(self)
        QApplication.instance().installEventFilter(self.global_event_filter)

    def eventFilter(self, obj, event):
        if obj == self.tree.viewport():
            if event.type() == QEvent.Type.MouseButtonPress:
                self._suppress_hide = True  # 激活关闭抑制
                pos = event.position().toPoint()
                item = self.tree.itemAt(pos)
                
                if item and self.tree.visualItemRect(item).contains(pos):
                    current_state = item.checkState(0)
                    new_state = Qt.CheckState.Checked if current_state == Qt.CheckState.Unchecked else Qt.CheckState.Unchecked
                    item.setCheckState(0, new_state)
                    return True
                
                if item and pos.x() < self.tree.indentation():
                    item.setExpanded(not item.isExpanded())
                    return True
                
        return super().eventFilter(obj, event)

    def hidePopup(self):
        if self._suppress_hide:
            self._suppress_hide = False
            return
        super().hidePopup()
        self._popup_open = False

    def showPopup(self):
        super().showPopup()
        self.tree.setFocus()
        self._popup_open = True

    # 其他方法保持不变（handle_item_changed, update_display等）

    def get_selected_hierarchy(self):
        """获取结构化选中数据"""
        result = []
        
        def parse_item(item):
            return {
                "text": item.text(0),
                "data": item.data(0, Qt.ItemDataRole.UserRole),
                "selected": item.checkState(0) == Qt.CheckState.Checked,
                "children": [parse_item(item.child(i)) 
                            for i in range(item.childCount())]
            }
        
        for i in range(self.tree.topLevelItemCount()):
            result.append(parse_item(self.tree.topLevelItem(i)))
        return result

    def _trigger_selection_change(self):
        """触发选择变更信号"""
        self.selection_changed.emit()

    def addItem(self, text, parent=None, data=None):
        item = QTreeWidgetItem(parent or self.tree)
        item.setText(0, text)
        item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsAutoTristate)
        item.setCheckState(0, Qt.CheckState.Unchecked)
        if data:
            item.setData(0, Qt.ItemDataRole.UserRole, data)
        return item
    
    def handle_item_changed(self, item):
        if item.checkState(0) == Qt.CheckState.Checked:
            for i in range(item.childCount()):
                child = item.child(i)
                child.setCheckState(0, Qt.CheckState.Checked)
        
        parent = item.parent()
        if parent:
            checked_count = 0
            for i in range(parent.childCount()):
                if parent.child(i).checkState(0) != Qt.CheckState.Unchecked:
                    checked_count += 1
            
            if checked_count == 0:
                parent.setCheckState(0, Qt.CheckState.Unchecked)
            elif checked_count == parent.childCount():
                parent.setCheckState(0, Qt.CheckState.Checked)
            else:
                parent.setCheckState(0, Qt.CheckState.PartiallyChecked)
        
        self.update_display()
        self._trigger_selection_change()  # 触发信号

    def update_display(self):
        selected = []
        def collect_checked_items(item):
            if item.checkState(0) == Qt.CheckState.Checked and not item.childCount():
                selected.append(item.text(0))
            for i in range(item.childCount()):
                collect_checked_items(item.child(i))
        
        for i in range(self.tree.topLevelItemCount()):
            collect_checked_items(self.tree.topLevelItem(i))
        
        self.lineEdit().setText(", ".join(selected))


class GlobalEventFilter(QObject):
    def __init__(self, combo):
        super().__init__()
        self.combo = combo

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Type.MouseButtonPress:
            if self.combo._popup_open:
                click_pos = event.globalPosition().toPoint()
                
                tree_geo = self.combo.tree.viewport().geometry()
                global_tree_pos = self.combo.tree.viewport().mapToGlobal(QPoint(0, 0))
                tree_rect = tree_geo.translated(global_tree_pos)
                
                combo_geo = self.combo.geometry()
                global_combo_pos = self.combo.mapToGlobal(QPoint(0, 0))
                combo_rect = combo_geo.translated(global_combo_pos)
                
                if not tree_rect.contains(click_pos) and not combo_rect.contains(click_pos):
                    self.combo.hidePopup()
                    return True  # 已处理外部点击
        return super().eventFilter(obj, event)

# 示例窗口和主程序代码保持不变
class DemoWindow(QWidget):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        
        self.combo = TreeCheckableComboBox()
        
        # 构建树形数据
        category1 = self.combo.addItem("硬件配置")
        self.combo.addItem("CPU", category1, "cpu_config")
        self.combo.addItem("内存", category1, "memory_config")
        
        category2 = self.combo.addItem("网络设置")
        self.combo.addItem("有线网络", category2, "lan_config")
        wifi = self.combo.addItem("无线网络", category2, "wifi_config")
        self.combo.addItem("2.4GHz", wifi, "2.4g_config")
        self.combo.addItem("5GHz", wifi, "5g_config")
        
        # 连接选择变更信号
        self.combo.selection_changed.connect(self._on_selection_changed)
        
        layout.addWidget(self.combo)
        self.setLayout(layout)

    def _on_selection_changed(self):
        """实时打印选中变更"""
        print("\n" + "-"*40)
        print("选项变更时当前选中项：")
        print(json.dumps(self.combo.get_selected_hierarchy(), indent=2, ensure_ascii=False))
        print("-"*40 + "\n")

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = DemoWindow()
    window.resize(400, 300)
    window.show()
    sys.exit(app.exec())